// 01-14-2007 Time: 1:46pm
package gamefiles;

import java.net.*;
import java.io.*;
import java.util.*;


public class BattleshipNetworkCom extends Object
{
        protected static final int BATTLESHIP_NETWORK_PORT = 4010;
        protected static final int BATTLESHIP_NETWORK_READ_TIMEOUT = 10;
        
        protected static final int ERROR_OK = 0;
        protected static final int ERROR_NOT_CONNECTED = -2;
        protected static final int ERROR_INVALID_ADDRESS = -3;
        protected static final int ERROR_INVALID_ARGUMENTS = -4;
        protected static final int ERROR_CONNECTION_FAILED = -5;
        protected static final int ERROR_ALREADY_CONNECTED = -6;
        protected static final int ERROR_INVALID_COMMAND = -7;
        protected static final int ERROR_IRRECOVERABLE_ERROR = -8;
        
        protected static final int COMMAND_READY = 1;
        protected static final int COMMAND_FIRE = 2;
        protected static final int COMMAND_SHIP_HIT = 3;
        protected static final int COMMAND_SHIP_MISS = 4;
        protected static final int COMMAND_VICTORY = 5;
        protected static final int COMMAND_CHAT = 6;
        protected static final int COMMAND_QUIT = 7;
        protected static final int COMMAND_TIMED_OUT = 8;
        
        public String errStr;
        public int errCode;
        protected NetworkClient client;
        protected NetworkServer server;
        protected boolean isServer;
        protected boolean isConnected;
        
        public BattleshipNetworkCom() {
                client = null;
                server = null;
                isConnected = false;
//                CSAGraphicLib.initLib();
        }
        
        /* Convenient function to set the error in case of an irrecoverable error. */
        private void setError(int errorCode, String errorString) {
                errCode = errorCode;
                errStr = errorString;
        }
        
        public int hostGame() {
                int retVal;
                
                if (isConnected == true)
                        return ERROR_ALREADY_CONNECTED;
                        
                isServer = true;
                
                if (server == null)
                        server = new NetworkServer(BATTLESHIP_NETWORK_PORT, BATTLESHIP_NETWORK_READ_TIMEOUT);
                
                retVal = server.init();
                if (retVal < ERROR_OK) {
                        setError(ERROR_IRRECOVERABLE_ERROR, server.errorToString());
                        return ERROR_IRRECOVERABLE_ERROR;
                }
                
                retVal = server.acceptClient();
                if (retVal < ERROR_OK) {
                        setError(ERROR_IRRECOVERABLE_ERROR, server.errorToString());
                        return ERROR_IRRECOVERABLE_ERROR;
                }
                
                isConnected = true;
                
                return ERROR_OK;        
        }
        
       public int connectGame(String address) {
                int retVal;
                
                if (isConnected == true)
                        return ERROR_ALREADY_CONNECTED;

                isServer = false;
                                
                if (client == null)
                        client = new NetworkClient(address, BATTLESHIP_NETWORK_PORT, BATTLESHIP_NETWORK_READ_TIMEOUT);
                
                if (client.isConnected())
                        return ERROR_ALREADY_CONNECTED;
                        
                retVal = client.connect();
                if (retVal < NetworkClient.ERROR_OK) {
                        if (retVal == NetworkClient.ERROR_UNKNOWN_HOST_EXCEPTION) {
                                return ERROR_INVALID_ADDRESS;
                        } else if (retVal == NetworkClient.ERROR_CONNECTION_FAILED) {
                                return ERROR_CONNECTION_FAILED;
                        } else {
                                setError(ERROR_IRRECOVERABLE_ERROR, client.errorToString());
                                return ERROR_IRRECOVERABLE_ERROR;
                        }
                }
                
                isConnected = true;
                
                return ERROR_OK;
        }
        
        public void disconnectGame() {
                isConnected = false;
                if (isServer == true) {
                        if (server == null)
                                return;
                        else if (server.isConnected())
                                server.disconnectClient();
                        server.close();
                } else {
                        if (client == null)
                                return;
                        else if (!client.isConnected())
                                return;
                        client.disconnect();
                }
        }
        
        private int assertConnected() {
                if (isConnected == false)
                        return ERROR_NOT_CONNECTED;
                        
                if (isServer == true) {
                        if (server == null)
                                return ERROR_NOT_CONNECTED;
                        else if (!server.isConnected())
                                return ERROR_NOT_CONNECTED;
                } else {
                        if (client == null)
                                return ERROR_NOT_CONNECTED;
                        else if (!client.isConnected())
                                return ERROR_NOT_CONNECTED;
                }
                return ERROR_OK;
        }
        
        private int writeCommand(String data) {
                int retVal;
                
                retVal = assertConnected();
                if (retVal != ERROR_OK)
                        return retVal;
                
                if (isServer == true) 
                        server.writeLine(data);                     
                else 
                        client.writeLine(data);
                        
                return ERROR_OK;
        }
        
        private String readCommand() {
                int retVal;
                String strCommand;
                
                retVal = assertConnected();
                if (retVal != ERROR_OK) {
                        errCode = retVal;
                        return null;
                }
                
                if (isServer == true) {
                        strCommand = server.readLine();
                        if (strCommand == null) {
                             if (server.errCode == NetworkServer.ERROR_READ_TIMEOUT)
                                return "";
                                      
                             setError(ERROR_IRRECOVERABLE_ERROR, server.errorToString());                   
                        }
               } else {
                        strCommand = client.readLine();
                        if (strCommand == null) {
                             if (client.errCode == NetworkClient.ERROR_READ_TIMEOUT)
                                return "";
                                      
                             setError(ERROR_IRRECOVERABLE_ERROR, client.errorToString());                   
                        }
                }
                
                return strCommand;
        }
        
         public int sendCommand(int command, ArrayList<String> commandParamaters) 
         {
                switch (command) {
						case COMMAND_READY:
							return writeCommand("R");
						case COMMAND_SHIP_HIT:
							return writeCommand("X");
						case COMMAND_SHIP_MISS:
							return writeCommand("O");
						case COMMAND_VICTORY:
							return writeCommand("V");
						case COMMAND_QUIT:	
							return writeCommand("Q");
						case COMMAND_CHAT:
							if (commandParamaters.size() < 1)
								return ERROR_INVALID_ARGUMENTS;
							return writeCommand("C" + commandParamaters.get(0));
						case COMMAND_FIRE:
							if (commandParamaters.size() < 2)
								return ERROR_INVALID_ARGUMENTS;
							return writeCommand("F" + commandParamaters.get(0) + "," + commandParamaters.get(1)); 
						default:
							return ERROR_INVALID_COMMAND;
                }
        }
        
        public int recvCommand(ArrayList<String> commandParamaters) {
                String strCommand;
                char command;
                
                if (commandParamaters == null)
                        return ERROR_INVALID_ARGUMENTS;
                
                commandParamaters.clear();
                
                strCommand = readCommand();
                if (strCommand == null)
                        return errCode;
                else if (strCommand.equals(""))
                        return COMMAND_TIMED_OUT;
                
                command = strCommand.charAt(0);
                switch (command) {
                        case 'F':
                                String[] coordinates;
                                
                                strCommand = strCommand.substring(1);
                                coordinates = strCommand.split(",");
                                if (coordinates.length != 2)
                                        return ERROR_INVALID_COMMAND;
                                
                                commandParamaters.add(coordinates[0]);
                                commandParamaters.add(coordinates[1]);
                                return COMMAND_FIRE;
                        case 'C':
                                commandParamaters.add(strCommand.substring(1));
                                return COMMAND_CHAT;
                        case 'R':
                                return COMMAND_READY;
                        case 'X':
                                return COMMAND_SHIP_HIT;
                        case 'O':
                                return COMMAND_SHIP_MISS;
                        case 'V':
                                return COMMAND_VICTORY;
                        case 'Q':
                                return COMMAND_QUIT;

                        default:
                               return ERROR_INVALID_COMMAND;
                }        
        }      
}

/*
 * Battleship Game Protocol
 *
 * Client A     connects to     Client B
 * Client A and Client B setup ships.
 * Client A sends "R" (ready) when done
 * Client B sends "R" (ready) when done
 *
 * Client A fires on a ship, 
 *  sends "F23,22" to fire at position 23,22
 * Client B replies "X" for ship hit or "O" for ship miss
 *  *OR* Client B replies "V" for Victory
 *
 * Client B fires on a ship,
 *  sends "F12,5", to fire at position 12,5
 * Client A replies "X" for ship hit or "O" for ship miss
 *  *OR* Client A replies "V" for Victory
 *
 * For a chat message either client sends "C" plus the message.
 * For disconnections, either client sends "Q" for quit.
 *
 */
